//---------------------------------------------------------------------
// Author: Keith Hill
//
// Description: Class to implement the Send-SmtpMail cmdlet.
//
// Creation Date: Nov 1, 2006
//---------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Management.Automation;
using System.Net.Mail;
using System.Security;
using System.Net;

namespace Pscx.Commands
{
    [Cmdlet(VerbsCommunications.Send, "SmtpMail", DefaultParameterSetName = "Authenticated"),
     Description("Sends email via specified SMTP server to specified recipients."),
     DetailedDescription(@"Sends email via specified SMTP server to specified recipients. This cmdlet checks for existence of several preference that if set can make this cmdlet much easier to use.  Those preference variables are:

* $PscxSmtpHostPreference=""smtp.some.domain.com""
* $PscxSmtpPortPreference=587
* $PscxSmtpFromPreference=""john_doe@some.domain.com""")]
    public class SendSmtpMailCommand : PSCmdlet
    {
        private PSCredential _credentials;
        private string _host;
        private string _subject;
        private string _body;
        private string _from;
        private string[] _to;
        private string[] _cc = new string[0];
        private string[] _bcc = new string[0];
        private string[] _attachmentPaths = new string[0];
        private List<string> _resolvedPaths;
        private int _timeout = 100 * 1000;
        private int _portNumber = -1;
        private bool _isBodyHtml;
        private bool _anonymous;
        private bool _literalPathUsed;

        [ValidateNotNullOrEmpty,
         Parameter(HelpMessage = "Specifies which SMTP host to use to send the email."),
         DefaultValue("Value of $PscxSmtpHostPreference if defined, otherwise null."),
         AcceptsWildcards(false)]
        public string SmtpHost
        {
            get { return _host; }
            set { _host = value; }
        }

        [Parameter(HelpMessage = "Specifies which port to use on the SMTP host."),
         AcceptsWildcards(false),
         DefaultValue("Value of $PscxSmtpPortPreference if defined, otherwise 25.")]
        public int PortNumber
        {
            get { return _portNumber; }
            set { _portNumber = value; }
        }

        [Parameter(ParameterSetName = "Authenticated", 
                   HelpMessage = "Specifies credentials required to aunthenticate on the SMTP host."),
         Credential,
         ValidateNotNull]
        public PSCredential Credential
        {
            get { return _credentials; }
            set { _credentials = value; }
        }

        [Parameter(ParameterSetName = "Anonymous", 
                   HelpMessage = "Connect to SMTP host anonymously.")]
        public SwitchParameter Anonymous
        {
            get { return (SwitchParameter)_anonymous; }
            set { _anonymous = (bool)value; }
        }

        [ValidateNotNullOrEmpty,
         Parameter(HelpMessage = "Specifies who the email is from."),
         AcceptsWildcards(false),
         DefaultValue("Value of $PscxSmtpFromPreference if defined, otherwise null.")]
        public string From
        {
            get { return _from; }
            set { _from = value; }
        }

        [Parameter(Mandatory = true, HelpMessage = "Specifies who the email will be sent to."),
         AcceptsWildcards(false),
         ValidateNotNullOrEmpty]
        public string[] To
        {
            get { return _to; }
            set { _to = value; }
        }

        [Parameter(HelpMessage = "Specifies who to CC on the email."),
         AcceptsWildcards(false),
         ValidateNotNull]
        public string[] Cc
        {
            get { return _cc; }
            set { _cc = value; }
        }

        [Parameter(HelpMessage = "Specifies who to BCC on the email."),
         AcceptsWildcards(false),
         ValidateNotNull]
        public string[] Bcc
        {
            get { return _bcc; }
            set { _bcc = value; }
        }

        [Parameter(HelpMessage = "Specifies the subject of the email."),
         AcceptsWildcards(false)]
        public string Subject
        {
            get { return _subject; }
            set { _subject = value; }
        }

        [Parameter(HelpMessage = "Specifies the body of the email."),
         AcceptsWildcards(false)]
        public string Body
        {
            get { return _body; }
            set { _body = value; }
        }

        [ValidateNotNull,
         Parameter(HelpMessage = "Specifies paths to attachments for the email."),
         AcceptsWildcards(false)]
        public string[] AttachmentPaths
        {
            get { return _attachmentPaths; }
            set { _attachmentPaths = value; }
        }

        [ValidateNotNull,
         Parameter(HelpMessage = "Specifies literal paths (no wildcard expansion) to attachments for the email."),
         AcceptsWildcards(false)]
        public string[] AttachmentLiteralPaths
        {
            get { return _attachmentPaths; }
            set 
            { 
                _attachmentPaths = value;
                _literalPathUsed = true;
            }
        }

        [ValidateRange(0, Int32.MaxValue), 
         Parameter(HelpMessage = "Specifies timeout for SMTP Server.  Default is 100 seconds."), 
         DefaultValue(100)]
        public int Timeout
        {
            get { return _timeout; }
            set { _timeout = value; }
        }

        [Parameter(HelpMessage = "Indicates that the body of the message is HTML.")]
        public SwitchParameter HtmlBody
        {
            get { return (SwitchParameter)_isBodyHtml; }
            set { _isBodyHtml = (bool)value; }
        }

        protected override void BeginProcessing()
        {
            if (String.IsNullOrEmpty(_host))
            {
                _host = this.GetVariableValue("PscxSmtpHostPreference") as string;
                if (String.IsNullOrEmpty(_host))
                {
                    ArgumentException ex = new ArgumentException("Neither the -Host parameter was specified nor was the $PscxSmtpHostPreference variable set.");
                    WriteError(new ErrorRecord(ex, "SendEmailNoSmtpHostSpecified", ErrorCategory.InvalidData, null));
                }
                WriteDebug("Using SMTP host " + _host);
            }
            if (_portNumber == -1)
            {
                int? portNumber = this.GetVariableValue("PscxSmtpPortPreference", 25) as int?;
                _portNumber = portNumber.Value;
                WriteDebug("Using SMTP port number " + _portNumber.ToString());
            }
            if (String.IsNullOrEmpty(_from))
            {
                _from = this.GetVariableValue("PscxSmtpFromPreference") as string;
                if (String.IsNullOrEmpty(_from))
                {
                    ArgumentException ex = new ArgumentException("Neither the -From parameter was specified nor was the " +
                                                                 "$PscxSmtpFromPreference variable set.");
                    WriteError(new ErrorRecord(ex, "SendEmailNoSmtpFromAddressSpecified", ErrorCategory.InvalidData, null));
                }
                WriteDebug("Using 'From' address " + _from);
            }

            _resolvedPaths = Utils.ResolveFilePaths(this, _attachmentPaths, _literalPathUsed);
            if ((_attachmentPaths.Length > 0) && (_resolvedPaths.Count == 0))
            {
                ArgumentException ex = new ArgumentException("The specified attachment paths could not be resolved to a provider path.  " +
                                                             "Consider using the -AttachmentLiteralPaths parameter");
                WriteError(new ErrorRecord(ex, "SendEmailAttachmentsNotFound", ErrorCategory.ObjectNotFound, null));
            }
        }

        protected override void EndProcessing()
        {
            MailMessage message = new MailMessage();

            message.From = new MailAddress(_from);

            if (!String.IsNullOrEmpty(_subject))
            {
                message.Subject = _subject;
            }

            if (!String.IsNullOrEmpty(_body))
            {
                message.Body = _body;
            }

            message.IsBodyHtml = _isBodyHtml;

            foreach (string recipient in _to)
            {
                message.To.Add(recipient);
            }

            foreach (string recipient in _cc)
            {
                message.CC.Add(recipient);
            }

            foreach (string recipient in _bcc)
            {
                message.Bcc.Add(recipient);
            }

            foreach (string attachment in _resolvedPaths)
            {
                message.Attachments.Add(new Attachment(attachment));
            }

            SmtpClient smtpClient = new SmtpClient(_host);
            try
            {
                smtpClient.Timeout = _timeout;
                smtpClient.Port = _portNumber;

                if (_anonymous)
                {
                    smtpClient.Credentials = null;
                    smtpClient.UseDefaultCredentials = false;
                }
                else if (_credentials != null)
                {
                    smtpClient.Credentials = _credentials.GetNetworkCredential();
                }
                else
                {
                    smtpClient.Credentials = CredentialCache.DefaultNetworkCredentials;
                }

                smtpClient.Send(message);
            }
            catch (InvalidOperationException ex)
            {
                WriteError(new ErrorRecord(ex, "SendEmailInvalidOpError", ErrorCategory.InvalidData, smtpClient));
            }
            catch (SmtpFailedRecipientsException ex)
            {
                WriteError(new ErrorRecord(ex, "SendEmailFailedRecipientsError", ErrorCategory.NotSpecified, smtpClient));
            }
            catch (SmtpException ex)
            {
                WriteError(new ErrorRecord(ex, "SendEmailSmtpError", ErrorCategory.NotSpecified, smtpClient));
            }
        }
    }
}
